package nl.vu.few.ibivu.biomodels.server.petrinet;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import nl.vu.few.ibivu.biomodels.server.DAO;
import nl.vu.few.ibivu.biomodels.shared.Model;
import nl.vu.few.ibivu.biomodels.shared.PetrifierModelExecution;
import nl.vu.few.ibivu.jlemon.NodeMap;
import nl.vu.few.ibivu.jlemon.algorithms.Connectivity;
import nl.vu.few.ibivu.jlemon.listgraph.Node;

import com.google.appengine.api.taskqueue.DeferredTask;

public class PetrifierExecutionTask implements DeferredTask {
	private static final long serialVersionUID = -3596244025574293262L;

	private Model model;
	
	private PetrifierExecutionTask() {
		super();
	}
	
	public PetrifierExecutionTask(Model model) {
		this();
		if (model == null) throw new NullPointerException();
		if (model.getId() == null) throw new IllegalArgumentException();
		if (model.getData() == null) throw new IllegalArgumentException();
		this.model = model;
	}
	

	@Override
	public void run() {
		final Petrifier petrifier = new Petrifier().build(model.getData());
		StateSpace stateSpace = new StateSpace().build(petrifier);
		NodeMap<Node, Integer> compMap = new NodeMap<Node, Integer>(stateSpace);
		int compNum = Connectivity.stronglyConnectedComponents(stateSpace, compMap);
		HashMap<Integer, Boolean> termMap = new HashMap<Integer, Boolean>();
		int termNum = Connectivity.terminalComponents(stateSpace, compMap, termMap);
		ArrayList<Node> geneList = new ArrayList<Node>();
		Iterator<Node> geneIterator = petrifier.placeIterator();
		while (geneIterator.hasNext()) {
			geneList.add(geneIterator.next());
		}
		Collections.sort(geneList, new Comparator<Node>() {

			@Override
			public int compare(Node o1, Node o2) {
				String lo1 = petrifier.getLabel(o1);
				String lo2 = petrifier.getLabel(o2);
				if (lo1 == null) {
					if (lo2 == null) return 0;
					else return -lo2.compareTo(lo1);
				}
				return lo1.compareTo(lo2);
			}});
		
		// generate a list of id given the ordered list of genes
		List<String> nodeIdList = new ArrayList<String>();
		for (Node gene : geneList) {
			nodeIdList.add(petrifier.getOriginalId(gene));
		}
		
		// create a list of list of states
		List<List<List<Integer>>> sccList = new ArrayList<List<List<Integer>>>(compNum);
		for (int i = 0; i < compNum; i++) {
			sccList.add(new ArrayList<List<Integer>>());
		}
		
		// for each state in the state space
		for (Node node : compMap.keySet()) {
			// retrieve the corresponding marking
			Map<Node, Integer> marking = stateSpace.getMarking(node);
			// create a new state
			ArrayList<Integer> state = new ArrayList<Integer>(geneList.size());
			// for each gene in the state add its value
			for (Node gene : geneList) {
				state.add(marking.get(gene));
			}
			// retrieve the corresponding list of states in the scc list
			List<List<Integer>> scc = sccList.get(compMap.get(node));
			// add the state to the scc
			scc.add(state);
		}
		List<Integer> tsccList = new ArrayList<Integer>(termNum);
		for (int i : termMap.keySet()) {
			if (termMap.get(i)) {
				tsccList.add(i);
			}
		}
		PetrifierModelExecution execution = new PetrifierModelExecution(model, nodeIdList, sccList, tsccList);
		
		new DAO().ofy().put(execution);
	}

}
